//
// CATGL - Cheap Action-game Tool with WebGL.
//
// Copyright 2010, tmasda.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var vec2 = {};

// 2次元の２つのベクトルのなす角を-π～πで求める
vec2.calcAngle = function(vecA, vecB){
    var len = vec2.length(vecA) * vec2.length(vecB);
    var cosA = vec2.dot(vecA, vecB) / len;
    if (cosA > 1){
        cosA = 1;
    } else if (cosA < -1) {
        cosA = -1;
    }
    var angle = Math.acos(cosA);
    if (vec2.cross(vecA, vecB) < 0){
        angle = -angle;
    }
    return angle;
}

// ベクトルの長さ
vec2.length = function(vec){
	var x = vec[0], y = vec[1];
	return Math.sqrt(x*x + y*y);
}

// 内積
vec2.dot = function(vec, vec2){
	return vec[0]*vec2[0] + vec[1]*vec2[1];
};

// 外積 
vec2.cross = function(vec, vec2){
	return vec[0]*vec2[1] - vec[1]*vec2[0];
};

// 生成
vec2.create = function(vec) {
	var dest = new glMatrixArrayType(2);
	
	if(vec) {
		dest[0] = vec[0];
		dest[1] = vec[1];
	}
	
	return dest;
};

// 2つのベクトルのなす各を0～πで求める
vec3.calcAngle = function(vectA, vectB){
    var len = vec3.length(vectA) * vec3.length(vectB);
    var cosA = vec3.dot(vectA, vectB) / len;
    if (cosA > 1){
        cosA = 1;
    } else if (cosA < -1) {
        cosA = -1;
    }
    var angle = Math.acos(cosA);
    return angle;
}

/*
 * mat3.multiplyVec3
 * Transforms a vec3 with the given matrix
 *
 * Params:
 * mat - mat3 to transform the vector with
 * vec - vec3 to transform
 * dest - Optional, vec3 receiving operation result. If not specified result is written to vec
 *
 * Returns:
 * dest if specified, vec otherwise
 */
mat3.multiplyVec3 = function(mat, vec, dest) {
	if(!dest) { dest = vec; }
	
	var x = vec[0], y = vec[1], z = vec[2];
	
	dest[0] = mat[0]*x + mat[3]*y + mat[6]*z;
	dest[1] = mat[1]*x + mat[4]*y + mat[7]*z;
	dest[2] = mat[2]*x + mat[5]*y + mat[8]*z;
	
	return dest;
};

/*
 * mat3.multiplyVec3Array
 * Transforms vec3 Array with the given matrix
 *
 * Params:
 * mat - mat3 to transform the vector with
 * vec - vec3 Array to transform
 * dest - Optional, vec3 receiving operation result. If not specified result is written to vec
 *
 * Returns:
 * dest if specified, vec otherwise
 */
mat3.multiplyVec3Array = function(mat, vec, dest) {
    var numItems = vec.length / 3;
    var i;
	if(!dest) { dest = vec; }
	
    for (i = 0; i < numItems; i++){
        var x = vec[i*3+0], y = vec[i*3+1], z = vec[i*3+2];
        
        dest[i*3+0] = mat[0]*x + mat[3]*y + mat[6]*z;
        dest[i*3+1] = mat[1]*x + mat[4]*y + mat[7]*z;
        dest[i*3+2] = mat[2]*x + mat[5]*y + mat[8]*z;
    }
	
	return dest;
};
/*
 * mat3.transpose
 * Transposes a mat3 (flips the values over the diagonal)
 *
 * Params:
 * mat - mat3 to transpose
 * dest - Optional, mat3 receiving transposed values. If not specified result is written to mat
 *
 * Returns:
 * dest is specified, mat otherwise
 */
mat3.transpose = function(mat, dest) {
	// If we are transposing ourselves we can skip a few steps but have to cache some values
	if(!dest || mat == dest) { 
		var a01 = mat[1], a02 = mat[2];
		var a12 = mat[5];
		
		mat[1] = mat[3];
		mat[2] = mat[6];
		mat[3] = a01;
		mat[5] = mat[7];
		mat[6] = a02;
		mat[7] = a12;
		return mat;
	}
	
	//dest[0] = mat[0];
	dest[1] = mat[3];
	dest[2] = mat[6];
	dest[3] = mat[1];
	//dest[4] = mat[4];
	dest[5] = mat[7];
	dest[6] = mat[2];
	dest[7] = mat[5];
	//dest[8] = mat[8];
	return dest;
};


/*
 * mat3.createI
 * Creates a new instance of a identity mat3 using the default array type
 *
 * Params:
 *
 * Returns:
 * New mat3
 */
mat3.createI = function() {
	var dest = new glMatrixArrayType(9);
    mat3.identity(dest);
	
	return dest;
};


/*
 * mat4.createI
 * Creates a new instance of a identity mat4 using the default array type
 * Any javascript array containing at least 16 numeric elements can serve as a mat4
 *
 * Params:
 * mat - Optional, mat4 containing values to initialize with
 *
 * Returns:
 * New mat4
 */
mat4.createI = function() {
	var dest = new glMatrixArrayType(16);
    mat4.identity(dest);
	
	return dest;
};

/*
 * mat4.createTranslate
 * Create a new instance of a Translation matrix by the given vector
 *
 * Params:
 * vec - vec3 specifying the translation
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.createTranslate = function(vec) {
	var dest = mat4.create();
	//dest[0] = 1;
	//dest[1] = 0;
	//dest[2] = 0;
	//dest[3] = 0;
	//dest[4] = 0;
	//dest[5] = 1;
	//dest[6] = 0;
	//dest[7] = 0;
	//dest[8] = 0;
	//dest[9] = 0;
	//dest[10] = 1;
	//dest[11] = 0;
    //dest[12] = vec[0];
    //dest[13] = vec[1];
    //dest[14] = vec[2];
	//dest[15] = 1;
	//return dest;
    return mat4.setTranslate(vec, dest);
};

/*
 * mat4.setTranslate
 * Set Translation matrix by the given vector
 *
 * Params:
 * vec - vec3 specifying the translation
 * dest - mat4 receiving operation result. 
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.setTranslate = function(vec, dest) {
	dest[0] = 1;
	dest[1] = 0;
	dest[2] = 0;
	dest[3] = 0;
	dest[4] = 0;
	dest[5] = 1;
	dest[6] = 0;
	dest[7] = 0;
	dest[8] = 0;
	dest[9] = 0;
	dest[10] = 1;
	dest[11] = 0;
    dest[12] = vec[0];
    dest[13] = vec[1];
    dest[14] = vec[2];
	dest[15] = 1;
	
	return dest;
};

/*
 * mat4.createRotateX
 * Create a new istance of Rotation matrix by the given angle around the X axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.createRotateX = function(angle) {
	var dest = mat4.create();
	//var s = Math.sin(angle);
	//var c = Math.cos(angle);
	//
	//dest[0] = 1;
	//dest[1] = 0;
	//dest[2] = 0;
	//dest[3] = 0;
	//dest[4] = 0;
	//dest[5] = c;
	//dest[6] = s;
	//dest[7] = 0;
	//dest[8] = 0;
	//dest[9] = -s;
	//dest[10] = c;
	//dest[11] = 0;
    //dest[12] = 0;
    //dest[13] = 0;
    //dest[14] = 0;
	//dest[15] = 1;
	//return dest;
    return mat4.setRotateX(angle, dest);
};

/*
 * mat4.setRotateX
 * Set a Rotation matrix by the given angle around the X axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 * dest - mat4 receiving operation result. 
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.setRotateX = function(angle, dest) {
	var s = Math.sin(angle);
	var c = Math.cos(angle);
	
	dest[0] = 1;
	dest[1] = 0;
	dest[2] = 0;
	dest[3] = 0;
	dest[4] = 0;
	dest[5] = c;
	dest[6] = s;
	dest[7] = 0;
	dest[8] = 0;
	dest[9] = -s;
	dest[10] = c;
	dest[11] = 0;
    dest[12] = 0;
    dest[13] = 0;
    dest[14] = 0;
	dest[15] = 1;
	return dest;
};

/*
 * mat4.createRotateY
 * Create a new istance of Rotation matrix by the given angle around the Y axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.createRotateY = function(angle) {
	var dest = mat4.create();
    return mat4.setRotateY(angle, dest);
};

/*
 * mat4.setRotateY
 * Set a Rotation matrix by the given angle around the Y axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 * dest - mat4 receiving operation result. 
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.setRotateY = function(angle, dest) {
	var s = Math.sin(angle);
	var c = Math.cos(angle);
	
	dest[0] = c;
	dest[1] = 0;
	dest[2] = -s;
	dest[3] = 0;
	dest[4] = 0;
	dest[5] = 1;
	dest[6] = 0;
	dest[7] = 0;
	dest[8] = s;
	dest[9] = 0;
	dest[10] = c;
	dest[11] = 0;
    dest[12] = 0;
    dest[13] = 0;
    dest[14] = 0;
	dest[15] = 1;
	return dest;
};

/*
 * mat4.createRotateZ
 * Create a new istance of Rotation matrix by the given angle around the Z axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.createRotateZ = function(angle) {
	var dest = mat4.create();
    return mat4.setRotateZ(angle, dest);
};

/*
 * mat4.setRotateZ
 * Set a Rotation matrix by the given angle around the Z axis
 *
 * Params:
 * angle - angle (in radians) to rotate
 * dest - mat4 receiving operation result. 
 *
 * Returns:
 * dest if specified, mat otherwise
 */
mat4.setRotateZ = function(angle, dest) {
	var s = Math.sin(angle);
	var c = Math.cos(angle);
	
	dest[0] = c;
	dest[1] = s;
	dest[2] = 0;
	dest[3] = 0;
	dest[4] = -s;
	dest[5] = c;
	dest[6] = 0;
	dest[7] = 0;
	dest[8] = 0;
	dest[9] = 0;
	dest[10] = 1;
	dest[11] = 0;
    dest[12] = 0;
    dest[13] = 0;
    dest[14] = 0;
	dest[15] = 1;
	return dest;
};

/*
 * mat4.multiplyVec3Array
 * Transforms vec3 Array with the given matrix
 * 4th vector component is implicitly '1'
 *
 * Params:
 * mat - mat4 to transform the vector with
 * vec - vec3 to transform
 * dest - Optional, vec3 receiving operation result. If not specified result is written to vec
 *
 * Returns:
 * dest if specified, vec otherwise
 */
mat4.multiplyVec3Array = function(mat, vec, dest) {
    var numVertex = vec.length / 3;
    var i;
	if(!dest) { dest = vec; }
	
    for (i = 0; i < numVertex; i++){
        var x = vec[i*3+0], y = vec[i*3+1], z = vec[i*3+2];
        
        dest[i*3+0] = mat[0]*x + mat[4]*y + mat[8]*z + mat[12];
        dest[i*3+1] = mat[1]*x + mat[5]*y + mat[9]*z + mat[13];
        dest[i*3+2] = mat[2]*x + mat[6]*y + mat[10]*z + mat[14];
    }
	
	return dest;
};

mat4.calcLocalVec3 = function(mat, vec, dest){
    var mat3x3 = mat4.toMat3(mat);
    if (!dest){
        dest = vec;
    }
	
    mat3.multiplyVec3(mat3x3, vec, dest);
    return dest;
}

mat4.move = function(mat, vec, dest){
	if(!dest) {
        dest = mat; 
    } else {
        mat4.set(dest, mat);
    }
    var vec2 = vec3.create();
    mat4.calcLocalVec3(mat, vec, vec2);
    dest[12] += vec2[0];
    dest[13] += vec2[1];
    dest[14] += vec2[2];
    return dest;
};


